package accounts

import (
	"fmt"
	"gitee.com/wm1998/account/services"
	"gitee.com/wm1998/infra/base"
	"github.com/kataras/iris/core/errors"
	"github.com/shopspring/decimal"
	"sync"
)

// 应用服务/Service接口层的实现层，职责：用户接口层 -> 应用服务/Service接口层 -> 业务领域层 -> DAO层
type accountService struct {
}

// 配合services/accounts.go的base.Check(IAccountService) 这行代码，当没被实例化时，就通过此行代码进行实例化
var _ services.AccountService = new(accountService)

// 避免应用服务层接口的实现类被实例化多次
var once sync.Once

func init() {
	once.Do(func() {
		// 一行代码决定结构体：accountService实现了services.IAccountService接口
		services.IAccountService = new(accountService)
	})
}

// 账户创建接口实现
func (a *accountService) CreateAccount(dto services.AccountCreatedDTO) (*services.AccountDTO, error) {
	domain := accountDomain{}
	// 验证输入参数-使用第3方库：*validator.Validate的Struct()函数对输入参数进行校验
	// 验证输入参数的前提：对输入参数的成员，需要在它的Tag中使用了“validate”
	if err := base.ValidateStruct(&dto); err != nil {
		return nil, err
	}

	//验证账户是否存在和幂等性
	acc := domain.GetAccountByUserIdAndType(dto.UserId, services.AccountType(dto.AccountType))
	if acc != nil {
		return acc, errors.New(
			fmt.Sprintf("用户的该类型账户已经存在：username=%s[%s],账户类型=%d", dto.Username, dto.UserId, dto.AccountType))
	}

	//执行账户创建的业务逻辑
	amount, err := decimal.NewFromString(dto.Amount)
	if err != nil {
		return nil, err
	}
	account := services.AccountDTO{
		UserId:       dto.UserId,
		Username:     dto.Username,
		AccountType:  dto.AccountType,
		AccountName:  dto.AccountName,
		CurrencyCode: dto.CurrencyCode,
		Status:       1,
		Balance:      amount,
	}

	rdto, err := domain.Create(account) // 调用业务领域层完成具体的业务处理
	return rdto, err
}

// 转账接口实现
func (a *accountService) Transfer(dto services.AccountTransferDTO) (services.TransferedStatus, error) {
	//验证参数
	domain := accountDomain{}
	//验证输入参数
	if err := base.ValidateStruct(&dto); err != nil {
		return services.TransferedStatusFailure, err
	}

	// 对转入/转出时的ChangeType进行校验
	amount, err := decimal.NewFromString(dto.AmountStr)
	if err != nil {
		return services.TransferedStatusFailure, err
	}
	dto.Amount = amount
	if dto.ChangeFlag == services.FlagTransferOut { // 如果是转出
		if dto.ChangeType > 0 {
			return services.TransferedStatusFailure,
				errors.New("如果changeFlag为支出，那么changeType必须小于0") // 自定义错误
		}
	} else {
		if dto.ChangeType < 0 {
			return services.TransferedStatusFailure,
				errors.New("处理转入，要求changeType必须大于0")
		}
	}

	//执行转账逻辑
	status, err := domain.Transfer(dto) // 调用业务领域层完成具体的业务处理
	/*if status == services.TransferedStatusSuccess {
		backwardDto := dto
		backwardDto.TradeBody = dto.TradeTarget
		backwardDto.TradeTarget = dto.TradeBody
		backwardDto.ChangeType = -dto.ChangeType
		backwardDto.ChangeFlag = -dto.ChangeFlag
		status, err := domain.Transfer(backwardDto)
		return status, err
	}*/
	return status, err
}

// 储值接口实现（特殊的转账，因其交易主体和目标都是本账户，目的是充值）
func (a *accountService) StoreValue(dto services.AccountTransferDTO) (services.TransferedStatus, error) {
	// 直接忽略对入参校验，参加上边函数代码
	dto.TradeTarget = dto.TradeBody
	dto.ChangeFlag = services.FlagTransferIn
	dto.ChangeType = services.AccountStoreValue
	return a.Transfer(dto)
}

// 账户信息查询接口实现
func (a *accountService) GetEnvelopeAccountByUserId(userId string) *services.AccountDTO {
	// 直接忽略对入参校验，参加上边函数代码
	domain := accountDomain{}
	account := domain.GetEnvelopeAccountByUserId(userId)
	return account
}

// 查询账户信息-非实现接口方法
func (a *accountService) GetAccount(accountNo string) *services.AccountDTO {
	// 直接忽略对入参校验，参加上边函数代码
	domain := accountDomain{}
	return domain.GetAccount(accountNo)
}
